package com.seckill.service.impl;

import cn.hutool.core.util.StrUtil;
import com.seckill.activemq.ActivemqSender;
import com.seckill.domain.MiaoshaOrder;
import com.seckill.domain.MiaoshaUser;
import com.seckill.exception.GlobalException;
import com.seckill.rabbitmq.MiaoshaMessage;
import com.seckill.redis.GoodsKey;
import com.seckill.redis.MiaoshaKey;
import com.seckill.redis.RedisService;
import com.seckill.result.CodeMsg;
import com.seckill.result.Result;
import com.seckill.service.GoodsService;
import com.seckill.service.MiaoshaOrderService;
import com.seckill.service.MiaoshaService;
import com.seckill.service.OrderService;
import com.seckill.util.Md5Util;
import com.seckill.util.UUIDUtil;
import com.seckill.vo.GoodsVo;
import com.seckill.vo.OrderTypeEnum;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * @author yangjunwei
 * @date 2021/11/16 4:42 下午
 */
@Service
public class MiaoshaServiceImpl implements MiaoshaService, InitializingBean {

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    RedisService redisService;

    @Autowired
    MiaoshaOrderService miaoshaOrderService;

    @Autowired
    ActivemqSender mqSender;

    /**
     * 商品库存是否为空，本地缓存标记
     */
    private static final Map<Long, Boolean> LOCAL_OVER_MAP = new ConcurrentHashMap<>();

    /**
     * 在类初始化阶段，将商品库存存到map中
     */
    @Override
    public void afterPropertiesSet() {
        //1.商品库存存到缓存中
        List<GoodsVo> goodsList = goodsService.getGoodsList();
        if (CollectionUtils.isEmpty(goodsList)) {
            return;
        }
        for (GoodsVo goodsVo : goodsList) {
            //缓存中增加商品库存
            redisService.set(GoodsKey.getMiaoshaGoodsStock, GoodsKey.getGoodsKey(goodsVo.getId()), goodsVo.getStockCount());
            //标记商品库存不为空
            LOCAL_OVER_MAP.put(goodsVo.getId(), false);
        }
    }

    @Override
    public Result<Integer> domiaosha(MiaoshaUser miaoshaUser, String path, long goodsId) {
        //1.判断秒杀path是否一致
        checkMiaoshaPath(path, goodsId, miaoshaUser.getId());
        //2.判断商品库存是否为空。走本地内存,减少使用缓存
        checkLocalCacheGoodsStock(goodsId);
        //3.走缓存判断用户是否秒杀过（商品Id+用户Id）
        miaoshaOrderService.checkUserMiaosha(goodsId, miaoshaUser.getId());
        //4.redis缓存预减库存(原子操作)
        decrGoodsCache(goodsId);
        //5.入队(异步创建订单)
        sendMiaoshaMsg(goodsId, miaoshaUser);
        return Result.success(0);
    }

    private void checkMiaoshaPath(String path, Long goodsId, Long userId) {
        String miaoshaPathCache = redisService.get(MiaoshaKey.miaoshaPath, MiaoshaKey.getMiaoshaKey(goodsId, userId), String.class);
        if (!StrUtil.equals(miaoshaPathCache, path)) {
            throw new GlobalException(CodeMsg.MIAOSHAPATH_ERROR);
        }
    }

    private void checkLocalCacheGoodsStock(Long goodsId) {
        Boolean goodsOver = LOCAL_OVER_MAP.get(goodsId);
        if (goodsOver) {
            throw new GlobalException(CodeMsg.INVENTORY_SHORTAGE);
        }
    }

    private void decrGoodsCache(Long goodsId) {
        Long decr = redisService.decr(GoodsKey.getMiaoshaGoodsStock, GoodsKey.getGoodsKey(goodsId));
        if (decr < 0) {
            //本地内存，添加商品库存不足的标记
            LOCAL_OVER_MAP.put(goodsId, true);
            //库存不足
            throw new GlobalException(CodeMsg.MIAO_SHA_OVER);
        }
    }

    private void sendMiaoshaMsg(Long goodsId, MiaoshaUser miaoshaUser) {
        MiaoshaMessage miaoshaMessage = new MiaoshaMessage();
        miaoshaMessage.setGoodsId(goodsId);
        miaoshaMessage.setUser(miaoshaUser);
        miaoshaMessage.setOrderType(OrderTypeEnum.PEAK_ORDER.getOrderType());
        mqSender.sendMiaoshaMsg(miaoshaMessage);
    }

    /**
     * 秒杀是否失败？
     */
    @Override
    public boolean getGoodsOver(long userId, long goodsId) {
        Boolean result = redisService.get(MiaoshaKey.isGoodsOver, MiaoshaKey.getMiaoshaKey(goodsId, userId), Boolean.class);
        if (Objects.isNull(result)) {
            return false;
        }
        return result;
    }

    /**
     * 获取秒杀结果 成功：订单Id 失败：-1 进行中：0
     */
    @Override
    public long getMiaoshaResult(long userId, long goodsId) {
        MiaoshaOrder miaoshaOrder = miaoshaOrderService.selectUserOrderForCache(goodsId, userId);
        //成功下订单
        if (Objects.nonNull(miaoshaOrder)) {
            return miaoshaOrder.getOrderId();
        }
        boolean goodsOver = getGoodsOver(userId, goodsId);
        //-1:秒杀失败(库存不足); 0:秒杀中
        return goodsOver ? -1 : 0;
    }

    @Override
    public String getMiaoshaPath(long userId, long goodsId) {
        String miaoshaPath = Md5Util.md5(UUIDUtil.uuid() + goodsId);
        redisService.set(MiaoshaKey.miaoshaPath, MiaoshaKey.getMiaoshaKey(goodsId, userId), miaoshaPath);
        return miaoshaPath;
    }

    /**
     * 缓存中库存回退+1
     *
     * @param goodsId 商品Id
     */
    @Override
    public void incrStock(long goodsId) {
        //redis
        long incr = redisService.incr(GoodsKey.getMiaoshaGoodsStock, GoodsKey.getGoodsKey(goodsId));
        //本地内存
        if (incr > 0 && LOCAL_OVER_MAP.get(goodsId)) {
            LOCAL_OVER_MAP.put(goodsId, false);
        }
    }


}
